package main

import (
	"embed"
	"io"
	"io/fs"
	"net"
	"net/http"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"time"
	"xiaowu"
	"xiaowu/pkg/core"

	"github.com/gorilla/websocket"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/labstack/gommon/log"
)

//go:embed static
var static embed.FS

var dog = 0 // 0 init, 1 connected, 2 disconnected
func main() {
	e := echo.New()
	e.Logger.SetLevel(log.ERROR)
	e.Use(middleware.Recover())

	static, err := fs.Sub(static, "static")

	if nil != err {
		panic(err)
	}

	e.POST("/translate", func(c echo.Context) error {
		type Request struct {
			Word string
		}
		type Response struct {
			Word       string
			Translated string
			Comment    []string
			Debug      []string
			Err        string
		}

		req := Request{}
		c.Bind(&req)

		res := Response{Word: req.Word}
		translated, comment, debug, err := core.Translate(req.Word)
		res.Translated = translated
		res.Comment = comment
		res.Debug = debug
		if err != nil {
			res.Err = err.Error()
		}

		return c.JSON(http.StatusOK, res)
	})

	bootup := time.Now()
	e.POST("/version", func(c echo.Context) error {
		return c.JSON(http.StatusOK, xiaowu.Version)
	})
	e.POST("/latest", func(c echo.Context) error {
		type Response struct {
			Latest     string
			ReleaseURL string
		}
		url := "https://gitee.com/keqinxiaowu/xiaowu/raw/master/version.txt"
		release := "https://gitee.com/keqinxiaowu/xiaowu/releases"
		if res, err := http.Get(url); nil != err {
			goto unknown
		} else {
			defer res.Body.Close()

			if bytes, err := io.ReadAll(res.Body); nil != err {
				goto unknown
			} else {
				res := Response{
					Latest:     string(bytes),
					ReleaseURL: release,
				}
				return c.JSON(http.StatusOK, res)
			}
		}

	unknown:
		return c.JSON(http.StatusOK, Response{
			Latest:     "unknown",
			ReleaseURL: release,
		})
	})
	e.GET("/feeddog", func(c echo.Context) error {
		wsc, err := Upgrader.Upgrade(c.Response(), c.Request(), nil)
		if err != nil {
			return err
		}
		defer wsc.Close()
		dog = 1
		for {
			if _, _, err := wsc.NextReader(); nil != err {
				dog = 2
				bootup = time.Now()
				return err
			} else {
				dog = 1
				// do nothing
			}
		}
	})

	e.GET("/*", echo.WrapHandler(http.FileServer(fsWithDefault{fs: http.FS(static)})))

	listener, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	e.Listener = listener

	go func() {
		ticker := time.NewTicker(2 * time.Second)
		for range ticker.C {
			switch dog {
			case 0:
				if time.Since(bootup) > 15*time.Second {
					os.Exit(0)
				}
			case 1: // do nothing
			case 2:
				if time.Since(bootup) > 3*time.Second {
					os.Exit(0)
				}
			}
		}
	}()
	go func() {
		time.Sleep(200 * time.Millisecond)
		openBrowser("http://" + listener.Addr().String())
	}()
	e.Logger.Fatal(e.Start(""))
}

func openBrowser(url string) {
	switch runtime.GOOS {
	case "windows":
		if err := exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start(); nil != err {
			panic(err)
		}
	case "darwin":
		if err := exec.Command("open", url).Start(); nil != err {
			panic(err)
		}
	default:
		panic("unsupported GOOS: " + runtime.GOOS)
	}
}

type fsWithDefault struct {
	fs http.FileSystem
}

func (f fsWithDefault) Open(name string) (http.File, error) {
	if file, err := f.fs.Open(name); nil != err && strings.HasSuffix(name, "/") {
		return f.fs.Open("/index.html")
	} else {
		return file, err
	}
}

var Upgrader = websocket.Upgrader{
	EnableCompression: true,
	CheckOrigin: func(r *http.Request) bool {
		// TODO: 放行所有流量
		return true
	},
}
